import type { AxiosError, AxiosInstance, AxiosRequestConfig, AxiosResponse, InternalAxiosRequestConfig } from 'axios'
import axios from 'axios'
import { AxiosLoading } from './loading'
import { STORAGE_AUTHORIZE_KEY, useAuthorization } from '~/composables/authorization'
import { ContentTypeEnum, RequestEnum } from '~#/http-enum'
import router from '~/router'

export interface PageResponse<T> {
  list: T[]
  pageNum: number
  pageSize: number
  total: number
}
export interface ResponseBody<T = any> {
  code: number
  result?: T | PageResponse<T>
  remark: string
}

export interface ResponseBodyData<T=any> {
  list?: T[]
  pageNum?: number
  pageSize?: number
  total?: number
}

export interface RequestConfigExtra {
  token?: boolean
  customDev?: boolean
  loading?: boolean
}
const DEFAULT_MESSAGE = '服务错误'
const DEFAULT_DESCRIPTION = '服务器内部异常，请稍后重试！'
const instance: AxiosInstance = axios.create({
  // baseURL: import.meta.env.VITE_APP_BASE_API ?? '/',
  baseURL: import.meta.env.VITE_APP_BASE_URL,
  timeout: 60000,
  headers: { 'Content-Type': ContentTypeEnum.JSON },
})
const axiosLoading = new AxiosLoading()
const requestHandler = async (config: InternalAxiosRequestConfig & RequestConfigExtra): Promise<InternalAxiosRequestConfig> => {
  // 处理请求前的url
  if (
    import.meta.env.DEV
    && import.meta.env.VITE_APP_BASE_API_DEV
    && import.meta.env.VITE_APP_BASE_URL_DEV
    && config.customDev
  ) {
    //  替换url的请求前缀baseUrl
    config.baseURL = import.meta.env.VITE_APP_BASE_API_DEV
  }
  const token = useAuthorization()

  if (token.value && config.token !== false)
    config.headers.set(STORAGE_AUTHORIZE_KEY, token.value)

  // 增加多语言的配置
  config.headers.set('Accept-Language', 'zh-CN')
  if (config.loading)
    axiosLoading.addLoading()
  return config
}

const responseHandler = (response: any): ResponseBody | AxiosResponse | Promise<any> | any => {
  const { code, remark } = response.data
  if (code === 10000)
    return response.data
  const notification = useNotification()
  notification?.error({
    message: DEFAULT_MESSAGE,
    description: remark || DEFAULT_DESCRIPTION,
    duration: 3,
  })
  return Promise.reject(response.data)
}

const errorHandler = (error: AxiosError): Promise<any> => {
  const token = useAuthorization()
  const notification = useNotification()

  if (error.response) {
    const { data, status, statusText } = error.response as AxiosResponse<ResponseBody>
    notification?.error({
      message: status || DEFAULT_MESSAGE,
      description: data?.remark || statusText || DEFAULT_DESCRIPTION,
      duration: 3,
    })
    if (status === 401) {
      // 这里处理清空用户信息和token的逻辑，后续扩展
      token.value = null
      router.push({
        path: '/login',
        query: {
          redirect: router.currentRoute.value.fullPath,
        },
      }).then(() => {})
    }
  }
  else {
    notification?.error({
      message: DEFAULT_MESSAGE,
      description: error?.message || DEFAULT_DESCRIPTION,
      duration: 3,
    })
  }
  return Promise.reject(error)
}
interface AxiosOptions<T> {
  url: string
  params?: T
  data?: T
}
instance.interceptors.request.use(requestHandler)

instance.interceptors.response.use(responseHandler, errorHandler)

export default instance
const instancePromise = <R = any, T = any>(options: AxiosOptions<T> & RequestConfigExtra): Promise<ResponseBody<R>> => {
  const { loading } = options
  return new Promise((resolve, reject) => {
    instance.request(options).then((res) => {
      resolve(res as unknown as ResponseBody)
    }).catch((e: Error | AxiosError) => {
      reject(e)
    })
      .finally(() => {
        if (loading)
          axiosLoading.closeLoading()
      })
  })
}
export const useGet = <R = any, T = any>(url: string, params?: T, config?: AxiosRequestConfig & RequestConfigExtra): Promise<ResponseBody<R>> => {
  const options = {
    url,
    params,
    method: RequestEnum.GET,
    ...config,
  }
  return instancePromise<R, T>(options)
}

export const usePost = <R = any, T = any>(url: string, data?: T, config?: AxiosRequestConfig & RequestConfigExtra): Promise<ResponseBody<R>> => {
  const options = {
    url,
    data,
    method: RequestEnum.POST,
    ...config,
  }
  return instancePromise<R, T>(options)
}

export const usePut = <R = any, T = any>(url: string, data?: T, config?: AxiosRequestConfig & RequestConfigExtra): Promise<ResponseBody<R>> => {
  const options = {
    url,
    data,
    method: RequestEnum.PUT,
    ...config,
  }
  return instancePromise<R, T>(options)
}

export const useDelete = <R = any, T = any>(url: string, data?: T, config?: AxiosRequestConfig & RequestConfigExtra): Promise<ResponseBody<R>> => {
  const options = {
    url,
    data,
    method: RequestEnum.DELETE,
    ...config,
  }
  return instancePromise<R, T>(options)
}
